/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ca.trader.sale;

import ca.trader.comment.Comment;
import ca.trader.db.SaleTableConstraints;
import ca.trader.regions.Country;
import ca.trader.regions.Location;
import ca.trader.regions.Region;
import ca.trader.user.User;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.SequenceGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

/**
 * An entity class to represent Sales. Each sale can have an id, a seller, the
 * creation date, the modified date, a title, a description, a price, a Location
 * where the sale is happening (country, region, city) and the category of the
 * sale.
 *
 * @author Ahsan
 */
@Entity
@NamedQueries(
{
	 // Query for finding sale by seller.  Value is sorted by date when the sale was created in descending order.
	 @NamedQuery(name = SaleEntityConsts.NAMED_QUERY_FIND_SALE_BY_SELLER,
	 query = "SELECT s FROM Sale s WHERE s.seller.id = :" + SaleEntityConsts.NAMED_QUERY_FIND_SALE_BY_USER_PARAM_SELLER_ID + " ORDER BY s.id DESC"),
	 // Query for finding sale count by seller
	 @NamedQuery(name = SaleEntityConsts.NAME_QUERY_SALE_COUNT_BY_SELLER,
	 query = "SELECT COUNT (s.id) FROM Sale s WHERE s.seller.id = :" + SaleEntityConsts.NAME_QUERY_SALE_COUNT_BY_SELLER_PARAM_SELLER_ID),
	 @NamedQuery(name = SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_BY_USER,
	 query = "SELECT COUNT (u.id) FROM User u JOIN FETCH u.favouriteSales fs WHERE u.id = :" + SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_BY_USER_PARAM_USER_ID 
	 + " AND fs.id = :" + SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_BY_USER_PARAM_SALE_ID),
	 @NamedQuery(name = SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_LIST_BY_USER,
	 query = "SELECT fs FROM User u JOIN FETCH u.favouriteSales fs WHERE u.id = :" + SaleEntityConsts.NAMED_QUERY_FIND_FAVOURITED_SALE_LIST_BY_USER_PARAM_USER_ID 
	   + " ORDER BY fs.id DESC"),
	 @NamedQuery(name = SaleEntityConsts.NAMED_QUERY_FAVOURITED_SALE_COUNT_BY_USER,
	 query = "SELECT COUNT (fs.id) FROM User u JOIN FETCH u.favouriteSales fs WHERE u.id = :" + SaleEntityConsts.NAMED_QUERY_FAVOURITED_SALE_COUNT_BY_USER_PARAM_USER_ID)	   
	/*  ,
	@NamedQuery(name = SaleEntityConsts.NAMED_QUERY_SEARCH_SALE_BY_KEYWORDS,
	 query = "SELECT s FROM Sale s WHERE SQL('MATCH(description, title) AGAINST( ? )', :" + SaleEntityConsts.NAMED_QUERY_SEARCH_SALE_BY_KEYWORDS_PARAM_KEYWORD
	  + ")") */
})

@XmlRootElement
public class Sale implements Serializable
{

	 /**
	  * Stores the ID of the sale. This value is auto-generated and should not
	  * be set explicitly.
	  */
	 @Id
	 @SequenceGenerator(name = "SALE_ID_SEQ", allocationSize = 1, initialValue = 1)
	 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SALE_ID_SEQ")
	 @Column(length = SaleTableConstraints.CONSTRAINT_SALE_ID_MAX_LENGTH, nullable = false)
	 private Integer id;
	 /**
	  * Stores the seller of the sale. This user must be an existing user,
	  * otherwise an exception will be thrown.
	  */
	 @ManyToOne ( cascade ={
		  CascadeType.MERGE, CascadeType.REFRESH
	 }, fetch = FetchType.LAZY)
	 @JoinColumn(nullable = false)
	 private User seller;
	 /**
	  * Stores the creation date of the sale. This value is auto-generated and
	  * should not be set explicitly.
	  */
	 @Temporal(TemporalType.TIMESTAMP)
	 @Column(nullable = false)
	 private Date creationDate;
	 /**
	  * Stores the modified date of the sale. This value is auto-generated and
	  * should not be set explicitly.
	  */
	 @Temporal(TemporalType.TIMESTAMP)
	 @Column(nullable = false)
	 private Date modifiedDate;
	 /**
	  * The title of the sale. THis value must be a maximum length of
	  * SaleTableConstraints.CONSTRAINT_SALE_TITLE_MAX_LENGTH
	  */
	 @Column(length = SaleTableConstraints.CONSTRAINT_SALE_TITLE_MAX_LENGTH, nullable = false)
	 private String title;
	 /**
	  * The description of the sale. THis value must be a maximum length of
	  * SaleTableConstraints.CONSTRAINT_SALE_DESC_MAX_LENGTH
	  */
	 @Column(length = SaleTableConstraints.CONSTRAINT_SALE_DESC_MAX_LENGTH, nullable = false)
	 private String description;
	 /**
	  * The price of the sale
	  */
	 @Column(nullable = false, precision = SaleTableConstraints.CONSTRAINT_SALE_PRICE_MAX_INT_PART_LENGTH, scale = SaleTableConstraints.CONSTRAINT_SALE_PRICE_MAX_DECIMAL_PART_LENGTH)
	 private BigDecimal price;
	 /**
	  * The location of the sale.
	  */
	 @Embedded
	 @Column(nullable = false)
	 private Location location;
	 /**
	  * The category of the same
	  */
	 @Basic
	 @Enumerated(EnumType.STRING)
	 @Column(nullable = false)
	 private SaleCategoryEnum category;
	 
	 @ManyToMany(mappedBy = "favouriteSales", cascade =
	 {
		  CascadeType.MERGE, CascadeType.REFRESH
	 }, fetch = FetchType.LAZY)
	 private List<User> favouritedByUsers = new ArrayList<>();
	 
	 @OneToMany (mappedBy = "sale", cascade =
	 {
		  CascadeType.MERGE, CascadeType.REFRESH
	 }, fetch = FetchType.LAZY)	 
	 private List<Comment> commentList = new ArrayList <>();

	 /**
	  *
	  * @return the date this sale was created
	  */
	 public Date getCreationDate()
	 {
		  return creationDate;
	 }

	 /**
	  *
	  * @return the date this sale was last modified
	  */
	 public Date getModifiedDate()
	 {
		  return modifiedDate;
	 }

	 /**
	  *
	  * @return the seller
	  */
	 public User getSeller()
	 {
		  return seller;
	 }

	 /**
	  *
	  * @return the user ID of the seller
	  */
	 public Integer getSellerID()
	 {
		  return seller.getId();
	 }

	 /**
	  * Sets the seller of the sale. An exception will be thrown if the seller
	  * does not already exist as a managed entity.
	  *
	  * @param seller the seller of the sale
	  */
	 public void setSeller(User seller)
	 {
		  this.seller = seller;
	 }

	 /**
	  * Gets the id of the sale
	  *
	  * @return the ID of the sale
	  */
	 public Integer getId()
	 {
		  return id;
	 }

	 /**
	  * Gets the title of the sale
	  *
	  * @return the title of the sale
	  */
	 public String getTitle()
	 {
		  return title;
	 }

	 /**
	  * sets the title of the sale
	  *
	  * @param title the title of the sale
	  */
	 public void setTitle(String title)
	 {
		  this.title = title;
	 }

	 /**
	  * Gets the description of the sale
	  *
	  * @return the description of the sale
	  */
	 public String getDescription()
	 {
		  return description;
	 }

	 /**
	  * sets the description of the sale
	  *
	  * @param title the description of the sale
	  */
	 public void setDescription(String description)
	 {
		  this.description = description;
	 }

	 /**
	  * Gets the price of the sale
	  *
	  * @return the price of the sale
	  */
	 public BigDecimal getPrice()
	 {
		  return price;
	 }

	 /**
	  * Sets the price of the sale
	  *
	  * @param price the price of the sale
	  */
	 public void setPrice(BigDecimal price)
	 {
		  this.price = price;
	 }

	 /**
	  * Gets the location of the sale
	  *
	  * @return the location of the sale
	  */
	 public Location getLocation()
	 {
		  return location;
	 }

	 /**
	  * Sets the location of the sale
	  *
	  * @param price the location of the sale
	  */
	 public void setLocation(Location location)
	 {
		  this.location = location;
	 }

	 /**
	  * Gets the city of the sale
	  *
	  * @return the city of the sale
	  */
	 public String getSellerCity()
	 {
		  return location.getCity();
	 }

	 /**
	  * Gets the region of the sale
	  *
	  * @return the region of the sale
	  */
	 public Region getSellerRegion()
	 {
		  return location.getRegion();
	 }

	 /**
	  * Gets the country of the sale
	  *
	  * @return the country of the sale
	  */
	 public Country getSellerCountry()
	 {
		  return location.getCountry();
	 }

	 /**
	  * Gets the category of the sale
	  *
	  * @return the category of the sale
	  */
	 public SaleCategoryEnum getCategory()
	 {
		  return category;
	 }

	 /**
	  * Sets the category of the sale
	  *
	  * @param category the category of the sale
	  */
	 public void setCategory(SaleCategoryEnum category)
	 {
		  this.category = category;
	 }

	 /**
	  * Private method to set the created date of the sale. Called
	  * automatically (no explicit call)
	  */
	 @PrePersist
	 private void updateCreatedDate()
	 {
		  price = price.setScale(SaleTableConstraints.CONSTRAINT_SALE_PRICE_MAX_DECIMAL_PART_LENGTH, RoundingMode.HALF_EVEN);
		  creationDate = new Date();
		  modifiedDate = new Date();
	 }

	 /**
	  * Private method to set the modified date of the sale. Called
	  * automatically (no explicit call)
	  */
	 @PreUpdate
	 private void updateModifiedDate()
	 {
		  modifiedDate = new Date();
	 }

	 public void addFavouritedUser(User user)
	 {
		  if (user != null)
		  {
			   favouritedByUsers.add(user);
		  }
		  else
		  {
			   throw new IllegalArgumentException("User object is null!");
		  }
	 }

	 @Override
	 public boolean equals(Object o)
	 {
		  if (!(o instanceof Sale))
		  {
			   return false;
		  }

		  Sale sale = (Sale) o;

		  return this.getId().equals(sale.getId());
	 }

	 public List<User> getFavouritedUsers()
	 {
		  return Collections.unmodifiableList(favouritedByUsers);
	 }

	 public boolean isFavouritedByUser(User user)
	 {
		  return favouritedByUsers.contains(user);
	 }
	 
	 public boolean isFavouritedByUserID (Integer userID)
	 {
		  for (User user:favouritedByUsers)
		  {
			   if (user.getId().equals(userID))
			   {
				    return true;
			   }
		  }
		  
		  return false;
	 }

	 public boolean unfavouriteUser(User user)
	 {
		  if (user != null)
		  {
			   return favouritedByUsers.remove(user);
		  }
		  else
		  {
			   throw new IllegalArgumentException("User object is null!");
		  }
	 }
	 
	 @XmlTransient
	 public List<Comment> getCommentList()
	 {
		  return Collections.unmodifiableList(commentList);
	 }
	 
	 public void addComment (Comment comment)
	 {
		  if (comment != null)
		  {
			   commentList.add(comment);
		  }
		  else
		  {
			   throw new IllegalArgumentException ("comment object is null!");
		  }
	 }

}
